package com.aica.filter;



import com.aica.constant.RedisTimeConstant;
import com.aica.constant.ResultConstant;
import com.aica.constant.StringBuilderConstant;
import com.aica.util.JwtTokenUtil;
import com.aica.util.RedisUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


/**
 * 过滤器校验token
 * @author yly
 * @date 2021/02/08
 */
@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    /**
     * 因为JWTAuthenticationFilter无空参构造函数导致无法使用Spring进行自动注入扫描
     */
    private RedisUtil redisUtil = null ;


    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager,authenticationEntryPoint);
    }
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, RedisUtil redisUtil) {
        super(authenticationManager);
        this.redisUtil = redisUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader("Authorization");
        //解析请求头Authorization
        if (header == null || !header.startsWith(StringBuilderConstant.TARGET)) {
            chain.doFilter(request, response);
            return;
        }
        //解析并校验token时效
        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
        if(authentication==null){
            log.info("密匙过期");
            response.setStatus(HttpStatus.OK.value());
            response.setContentType("text/json;charset=utf-8");
            response.getWriter().write(new ObjectMapper().writeValueAsString(ResultConstant.TOKEN_EXPIRE));
            return;
        }
        //token存在且未过期
        SecurityContextHolder.getContext().setAuthentication(authentication);

        chain.doFilter(request, response);

    }

    /**
     * 自定义获取token解析操作，同时对token进行有效期调整
     * @param request 请求
     * @return 用户名密码校验token
     */
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (token == null) {
            return null;
        }

        //解析token.
        String userName = JwtTokenUtil.getUserNameForToken(token);
        List<Integer> roleIds = JwtTokenUtil.getRoleIdsForToken(token);
        if (userName == null ||roleIds==null) {
            return null;
        }
        //更新与过期校验
        String userTokenKey = StringBuilderConstant.USER_TOKEN_ + userName;
        boolean hasKey = redisUtil.hasKey(userTokenKey);
        if(!hasKey){
            return null;
        }
        //token时效小于3天，自动调整为15天重新计时
        if(redisUtil.getExpire(userTokenKey)<= RedisTimeConstant.TOKEN_MIN_TIME_OUT){
            redisUtil.expire(userTokenKey,RedisTimeConstant.TOKEN_TIME_OUT);
        }

        return new UsernamePasswordAuthenticationToken(userName, null, getAuthority(roleIds));
    }


    public List<SimpleGrantedAuthority> getAuthority(List<Integer> roleIds) {

        List<SimpleGrantedAuthority> list = new ArrayList<>();

        //使用 用户等级进行拼接
        for (Integer roleId : roleIds) {
            list.add(new SimpleGrantedAuthority(StringBuilderConstant.ROLE_ + roleId));
        }

        return list;
    }
}
